"""
Utility script to generate offset definitions for a peripheral from an SVD file.

Usage:

    python gen_offsets.py \
        --svd <svd_file> \
        --output <output_file> \
        --peripheral <peripheral_name> \
        --guard <header_guard> \
        [--prefix <prefix>]

Copyright (c) 2024 Nordic Semiconductor ASA
SPDX-License-Identifier: Apache-2.0
"""

import argparse
from datetime import datetime
from pathlib import Path
import sys
import xml.etree.ElementTree as ET


HEADER = """/*
 * Copyright (c) {} Nordic Semiconductor ASA
 * SPDX-License-Identifier: Apache-2.0
 */

/* autogenerated using Nordic HAL utils/gen_offsets.py script */
"""


def parse_offsets(offsets, tree, base_name, base_offset):
    if base_name:
        base_name += "_"

    entries = tree.findall("cluster") + tree.findall("register")
    for entry in entries:
        is_cluster = entry.tag == "cluster"
        name = base_name + entry.find("name").text
        offset = base_offset + int(entry.find("addressOffset").text, 0)

        if "[%s]" not in name:
            if is_cluster:
                parse_offsets(offsets, entry, name, offset)
            else:
                offsets.append((name, offset))
        else:
            name = name.replace("[%s]", "_{}")
            dim = int(entry.find("dim").text, 0)
            dim_inc = int(entry.find("dimIncrement").text, 0)
            for i in range(dim):
                if is_cluster:
                    parse_offsets(offsets, entry, name.format(i), offset + i * dim_inc)
                else:
                    offsets.append((name.format(i), offset + i * dim_inc))


def gen_offsets(svd, output, peripheral, guard, prefix):
    root = ET.parse(svd).getroot()
    registers = root.find(f".//peripheral[name='{peripheral}']/registers")
    if not registers:
        sys.exit(f"No registers found for {peripheral}")

    offsets = []
    parse_offsets(offsets, registers, "", 0)

    with open(output, "w") as f:
        f.write(HEADER.format(datetime.now().year))
        f.write(f"\n#ifndef {guard}\n")
        f.write(f"#define {guard}\n\n")

        width = max(len(offset[0]) for offset in offsets)
        for offset in offsets:
            f.write(f"#define {prefix}{offset[0].ljust(width)} 0x{offset[1]:03X}U\n")

        f.write(f"\n#endif /* {guard} */\n")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(allow_abbrev=False)
    parser.add_argument("--svd", type=Path, required=True, help="SVD file")
    parser.add_argument("--output", type=Path, required=True, help="Output file")
    parser.add_argument("--peripheral", type=str, required=True, help="Peripheral")
    parser.add_argument("--guard", type=str, required=True, help="Header guard")
    parser.add_argument("--prefix", type=str, default="", help="Prefix register names")
    args = parser.parse_args()

    gen_offsets(args.svd, args.output, args.peripheral, args.guard, args.prefix)
